package com.vanguard.codec;

import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.session.AttributeKey;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.CumulativeProtocolDecoder;
import org.apache.mina.filter.codec.ProtocolDecoderOutput;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @Title: 客户端解码器 解码方式为 不定长
 * @Description: 解码Server的响应报文给Client
 * @Author: vanguard
 * @Version: 1.0
 * @Date: 18/12/27
 */
public class ClientNotFixedLengthDecode extends CumulativeProtocolDecoder {
    private final String charset;

    /**
     * 循环体长度字段是从第几位开始
     */
    private final int loopLengthIndex = 7;

    private final String separator = ";";
    /**
     * 报文结束符
     */
    private final String tailed = ";|";

    private final int loopLength = 5;

    private final int headLength = 2;
    private final int loopNumStartIndex = 7;//循环数开始位置
    private final int loopNumLength = 5;//循环数长度
    
    private final static Logger logger = LoggerFactory.getLogger(ClientNotFixedLengthDecode.class);
    
    //注意这里使用了Mina自带的AttributeKey类来定义保存在IoSession中对象的键值,其可有效防止键值重复  
    private final AttributeKey CONTEXT = new AttributeKey(getClass(), "context");
    
    public ClientNotFixedLengthDecode(String charset){
        this.charset = charset;

    }  
    private Context getContext(IoSession session){
        Context context = (Context)session.getAttribute(CONTEXT);  
        if(null == context){  
            context = new Context();  
            session.setAttribute(CONTEXT, context);  
        }  
        return context;  
    }  
    @Override  
    protected boolean doDecode(IoSession session, IoBuffer in, ProtocolDecoderOutput out) throws Exception {
        Context ctx = this.getContext(session);  
        IoBuffer buffer = ctx.innerBuffer;
        int messageCount = ctx.getMessageCount();  
        int efectCount = headLength;

        //判断position和limit之间是否有元素
        while(in.hasRemaining()){

            //get()读取buffer的position的字节,然后position+1
            buffer.put(in.get());

            //约定:报文的tillLoopNumLengthByte个字符的最后loopNumLength个字符表示循环体的循环次数
            if(messageCount++ == (loopNumStartIndex + loopNumLength -1)){
                buffer.flip();       
                byte[] tillLoopNumLengthByte = new byte[loopNumStartIndex + loopNumLength];
                buffer.get(tillLoopNumLengthByte); 
                byte[] loopNumByte = new byte[loopNumLength];
                //采用字节截取 
                System.arraycopy(tillLoopNumLengthByte, loopNumStartIndex, loopNumByte, 0, loopNumLength);
                String loopNum = new String(loopNumByte,charset);
                
                efectCount = headLength + Integer.parseInt(loopNum.trim())*loopLength ;
                try{  
                    ctx.setMessageLength(efectCount);
                } catch(NumberFormatException e){
                	logger.error("报文长度转换出错！" ,e);
                    ctx.setMessageLength(in.limit());  
                }
                buffer.limit(in.limit()); //让两个IoBuffer的limit相等  
            }  
        }  
        ctx.setMessageCount(messageCount);

        if(messageCount <= buffer.position()&&efectCount <= messageCount){
            buffer.flip();
            byte[] message = new byte[efectCount];
            buffer.get(message);
            out.write(new String(message, charset));
            ctx.reset();
            return true;
            //传过来的报文长度可能小于报文长度
        }else{
            return false;
        }
        
    }  
    private class Context{  
        private final IoBuffer innerBuffer; //用于累积数据的IoBuffer
        private int messageCount;           //记录已读取的报文字节数  
        private int messageLength;          //记录已读取的报文头标识的报文长度  
        public Context(){  
            innerBuffer = IoBuffer.allocate(100).setAutoExpand(true);
        }  
        public int getMessageCount() {  
            return messageCount;  
        }  
        public void setMessageCount(int messageCount) {  
            this.messageCount = messageCount;  
        }  
        public int getMessageLength() {  
            return messageLength;  
        }  
        public void setMessageLength(int messageLength) {  
            this.messageLength = messageLength;  
        }  
        public void reset(){  
            this.innerBuffer.clear(); //Set limit=capacity and position=0 and mark=-1  
            this.messageCount = 0;  
            this.messageLength = 0;  
        }  
    }  
}
